/**************************************************************************
 *
 * Copyright (C) 2005 Steve Karg
 *
 * SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
 *
 *********************************************************************/
#include <stdint.h> /* for standard integer types uint8_t etc. */
#include <stdbool.h> /* for the standard bool type. */
#include "bacnet/bacdcode.h"
#include "bacnet/datalink/bip.h"
#include "eth.h"
#include "bacport.h" /* custom per port */

static int BIP_Socket = -1;
/* port to use - stored in host byte order */
static uint16_t BIP_Port = 0xBAC0U;
/* IP Address - stored in host byte order */
static struct in_addr BIP_Address;
/* Broadcast Address - stored in host byte order */
static struct in_addr BIP_Broadcast_Address;

void bip_set_socket(int sock_fd)
{
    BIP_Socket = sock_fd;
}

int bip_socket(void)
{
    return BIP_Socket;
}

bool bip_valid(void)
{
    return (BIP_Socket != -1);
}

void bip_cleanup(void)
{
    /*    if (bip_valid()) */
    /*        close(BIP_Socket); */
    BIP_Socket = -1;

    return;
}

/* set using network byte order */
void bip_set_addr(uint32_t net_address)
{
    /*    BIP_Address.s_addr = ntohl(net_address); */
    BIP_Address.s_addr = net_address;
}

/* returns host byte order */
uint32_t bip_get_addr(void)
{
    return BIP_Address.s_addr;
}

/* set using network byte order */
void bip_set_broadcast_addr(uint32_t net_address)
{
    /*    BIP_Broadcast_Address.s_addr = ntohl(net_address); */
    BIP_Broadcast_Address.s_addr = net_address;
}

/* returns host byte order */
uint32_t bip_get_broadcast_addr(void)
{
    return BIP_Broadcast_Address.s_addr;
}

/* set using host byte order */
void bip_set_port(uint16_t port)
{
    BIP_Port = port;
}

/* returns host byte order */
uint16_t bip_get_port(void)
{
    return BIP_Port;
}

/* function to send a packet out the BACnet/IP socket (Annex J) */
/* returns number of bytes sent on success, negative number on failure */
int bip_send_pdu(BACNET_ADDRESS *dest, /* destination address */
    BACNET_NPDU_DATA *npdu_data, /* network information */
    uint8_t *pdu, /* any data to be sent - may be null */
    unsigned pdu_len)
{ /* number of bytes of data */

    struct sockaddr_in bip_dest;
    uint8_t mtu[4];
    int mtu_len = 0;
    int bytes_sent = 0;
    UDP_HDR udphdr;
    IP_HDR iphdr;
    uint8_t mac[6];

    (void)npdu_data;

    /* assumes that the driver has already been initialized */
    /*  if (BIP_Socket < 0) */
    /*      return BIP_Socket; */

    mtu[0] = BVLL_TYPE_BACNET_IP;
    bip_dest.sin_family = AF_INET;

    if (dest->mac_len == 6) {
        memcpy(&(bip_dest.sin_addr.s_addr), &dest->mac[0], 4);
        decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port));
        memset(&(bip_dest.sin_zero), '\0', 8);
        mtu[1] = BVLC_ORIGINAL_UNICAST_NPDU;
    }
    /* broadcast */
    else if (dest->mac_len == 0) {
        bip_dest.sin_addr.s_addr = BIP_Broadcast_Address.s_addr;
        bip_dest.sin_port = htons(BIP_Port);
        memset(&(bip_dest.sin_zero), '\0', 8);
        mtu[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
    } else
        return -1;

    mtu_len = 2;
    mtu_len += encode_unsigned16(
        &mtu[mtu_len], (uint16_t)(pdu_len + 4 /*inclusive */));
    mtu_len += pdu_len;

    /* IP address should be in network byte order */
    ARPIsResolved(bip_dest.sin_addr.s_addr, mac);

    iphdr.vhl = 0x45;
    iphdr.tos = 0;
    iphdr.iplen = htons(mtu_len + sizeof(IP_HDR) + sizeof(UDP_HDR));
    ++ipid;
    iphdr.ipid = htons(ipid);
    iphdr.ipoffset[0] = iphdr.ipoffset[1] = 0;
    iphdr.ttl = UIP_TTL;
    iphdr.proto = UIP_PROTO_UDP;
    iphdr.ipchksum = 0;
    iphdr.srcipaddr = BIP_Address.s_addr;
    iphdr.destipaddr = bip_dest.sin_addr.s_addr;

    /* Calculate IP checksum. */
    iphdr.ipchksum = ~(ip_getchksum((uint8_t *)&iphdr));

    /* Ports are sent in Network byte order.  BIP_Port is stored in Host byte
     * order */
    udphdr.srcport = htons(BIP_Port);
    udphdr.destport = bip_dest.sin_port;
    udphdr.udplen = htons(mtu_len + sizeof(UDP_HDR));
    /* Not using UDP checksums */
    udphdr.udpchksum = 0;

    EthernetSendHeader(mac, UIP_ETHTYPE_IP);
    EthernetSend((uint8_t *)&iphdr, sizeof(IP_HDR));
    EthernetSend((uint8_t *)&udphdr, sizeof(UDP_HDR));
    EthernetSend(mtu, 4);
    EthernetSend(pdu, pdu_len);
    EthernetFlush();
    uip_stat.ip.sent++;
}

/* receives a BACnet/IP packet */
/* returns the number of octets in the PDU, or zero on failure */
uint16_t bip_receive(BACNET_ADDRESS *src, /* source address */
    uint8_t *pdu, /* PDU data */
    uint16_t max_pdu, /* amount of space available in the PDU  */
    unsigned timeout)
{ /* number of milliseconds to wait for a packet */
    return 0;
}

void bip_get_my_address(BACNET_ADDRESS *my_address)
{
    int i = 0;

    my_address->mac_len = 6;
    (void)encode_unsigned32(&my_address->mac[0], htonl(BIP_Address.s_addr));
    (void)encode_unsigned16(&my_address->mac[4], htons(BIP_Port));
    my_address->net = 0; /* local only, no routing */
    my_address->len = 0; /* no SLEN */
    for (i = 0; i < MAX_MAC_LEN; i++) {
        /* no SADR */
        my_address->adr[i] = 0;
    }

    return;
}

void bip_get_broadcast_address(BACNET_ADDRESS *dest)
{ /* destination address */
    int i = 0; /* counter */

    if (dest) {
        dest->mac_len = 6;
        (void)encode_unsigned32(
            &dest->mac[0], htonl(BIP_Broadcast_Address.s_addr));
        (void)encode_unsigned16(&dest->mac[4], htons(BIP_Port));
        dest->net = BACNET_BROADCAST_NETWORK;
        dest->len = 0; /* no SLEN */
        for (i = 0; i < MAX_MAC_LEN; i++) {
            /* no SADR */
            dest->adr[i] = 0;
        }
    }

    return;
}
